<script lang="ts" setup>
import { computed, onMounted, onUnmounted, ref, unref } from "vue";
import { exportJson, importJson } from "@/utils/exportJson";
import { emitter, openSelectDirectory } from "@/utils/mitt";
import { ElMessage, ElNotification, UploadFile } from "element-plus";
import RunningIndexDialog from "./components/RunningIndexDialog.vue";
import { runStorage } from "@/storage";
import { useRoute } from "vue-router";
import { refreshFormatTime, toFormatTime } from "@/utils";
import { sleep } from "@/utils/time";
import { useDebounceFn, useStorage } from "@vueuse/core";
import {
  onDotnetPublishExec,
  onDotnetRunExec,
  onStartExec,
  onStartShellExec,
  onTaskkill,
  onZipPack,
} from "@/utils/upload";
import {
  Delete,
  Download,
  Edit,
  Files,
  Plus,
  Position,
  SwitchButton,
  Upload,
  VideoPlay,
} from "@element-plus/icons-vue";
import { handleAppStaticPath } from "@/demos/ipc";

const route = useRoute();

const dialogRef = ref<typeof RunningIndexDialog>();
const tags = ref<string[]>([]);
const checkedTag = ref();
const cmdMessage = ref<string>("");

const emitKey = `on-directory-${route.fullPath}`;
const emitVisibleKey = `on-visible-${route.fullPath}`;
// 是否以分离模式运行
const detached = useStorage("running-detached", false);

const clearMessage = useDebounceFn(() => {
  cmdMessage.value = "";
}, 5000);

const handleExportJson = () => {
  openSelectDirectory();
};

const handleImportJson = (file: UploadFile) => {
  importJson(file).then((json) => {
    if (json && json.length) {
      console.log("导入的JSON数据：", json);
      runStorage.value = json;
    }
  });
};

const handleEditRow = (row?: RunType) => {
  dialogRef.value?.open(row);
};

const handleRemoveRow = (row: RunType) => {
  // 过滤
  runStorage.value = runStorage.value.filter((item) => item.id !== row.id);
  ElMessage.success("已删除运行记录");
};

const handleRunRow = async (item: RunType) => {
  if (item.taskkill) {
    onTaskkill(item.taskkill);
    item.running = false;
    item.taskkill = undefined;
    ElNotification({
      title: item.title,
      message: `${item.filename}：已关闭！`,
      position: "bottom-right",
    });
  } else {
    item.running = true;
    item.taskkill = onDotnetRunExec(item, detached.value, (type, message) => {
      if (type === "data") {
        cmdMessage.value = message;
        clearMessage();
      } else if (type === "error") {
        ElMessage.error(message);
      }
    });
  }
};

const handleStopAll = async () => {
  const list = runStorage.value.filter((f) => f.taskkill);
  if (list.length) {
    for (const item of list) {
      await handleRunRow(item);
      await sleep(300);
    }
  } else {
    for (const item of unref(runStorage)) {
      await handleRunRow(item);
      await sleep(300);
    }
  }
};

const handlePublishRow = (item: RunType) => {
  item.loading = true;
  onDotnetPublishExec(item, (error, stdout, stderr) => {
    if (error) {
      item.loading = false;
      ElMessage.error(`执行错误: ${error.message}`);
      return;
    }
    if (stderr) {
      item.loading = false;
      ElMessage.error(`命令错误: ${stderr}`);
      return;
    }
    item.loading = false;
    item.time = Date.now();
    item.time_string = toFormatTime(item.time);
    ElNotification({
      title: item.title,
      message: `${item.filename} 发布已完成！`,
      type: "success",
      position: "bottom-right",
    });
  });
};

const openDir = (item: RunType) => {
  if (item.source) {
    onStartExec(item.source);
  } else {
    ElMessage.warning("运行源未设置");
  }
};

const openHost = (item: RunType) => {
  if (item.host) {
    onStartShellExec(item.host);
  } else {
    ElMessage.warning("API地址未设置");
  }
};

/**
 * 压缩
 * @param alter 只传5分钟内变更的文件（不包含排除的文件）
 */
const handleZip = async (item: RunType, alter?: boolean | undefined) => {
  try {
    item.compression = true;
    // 避免压缩性能占用未显示加载中的问题
    await sleep(300);
    // 压缩
    const { output, target } = await onZipPack({
      web: item,
      alter,
      hash: true,
    });
    // 提示
    const message = `\n${item.title} - 压缩完成。\n${output}\n`;
    ElNotification({
      title: "成功提示",
      message,
      type: "success",
      position: "bottom-left",
    });
    onStartExec(target);
    item.compression = false;
  } catch (e) {
    const message = e ? String(e) : `\n${item.title} - 压缩失败\n`;
    item.compression = false;
    ElNotification({
      title: "错误提示",
      message,
      type: "error",
      duration: 0,
    });
  }
};

// const staticExpress: { [key: string]: any } = {};
const handleExpress = async (item: RunType) => {
  try {
    item.loading = true;
    const bool = await handleAppStaticPath(item);
    console.log(item);
    console.log("boolbool", bool);
    item.fileRunning = !item.fileRunning;
  } finally {
    item.loading = false;
  }
};

const loopItems = computed(() => {
  return checkedTag.value
    ? runStorage.value.filter((f) => f.tag === checkedTag.value)
    : runStorage.value;
});

const loopItemsCount = computed(() => {
  return loopItems.value.filter((f) => f.running || f.taskkill).length;
});

onMounted(() => {
  emitter.on(emitKey, (localPath) => {
    const filename = `${route.meta.title || "data"}.json`;
    exportJson(localPath, unref(runStorage), filename);
  });
  emitter.on(emitVisibleKey, (visible) => {
    if (visible) refreshFormatTime(unref(runStorage));
  });
  refreshFormatTime(unref(runStorage));

  runStorage.value.forEach((item) => {
    // item.running = false;
    item.loading = false;
    item.fileRunning = false;
    item.compression = false;
  });
  const list = runStorage.value
    .filter((f) => f.tag)
    .map((v) => v.tag) as string[];
  tags.value = [...new Set(list)];
});

onUnmounted(() => {
  emitter.off(emitKey);
  emitter.off(emitVisibleKey);
});
</script>

<template>
  <div class="running-view pa5">
    <div class="button-margin-0 flex align-center" style="gap: 5px">
      <el-button plain type="primary" :icon="Plus" @click="handleEditRow()">
        新增运行
      </el-button>
      <el-button plain type="info" :icon="Download" @click="handleExportJson()">
        导出JSON
      </el-button>
      <el-upload
        :auto-upload="false"
        @change="handleImportJson"
        :show-file-list="false"
      >
        <el-button plain type="warning" :icon="Upload"> 导入JSON </el-button>
      </el-upload>
      <el-button
        plain
        :type="loopItemsCount ? 'danger' : 'success'"
        :icon="VideoPlay"
        @click="handleStopAll()"
      >
        一键起停
      </el-button>
      <el-select
        v-model="checkedTag"
        size="small"
        clearable
        placeholder="标签筛选"
        style="width: 120px"
        class="flex-shrink"
      >
        <el-option v-for="tag in tags" :key="tag" :value="tag" :label="tag" />
      </el-select>
      <el-checkbox v-model="detached">弹出终端 [方便看日志]</el-checkbox>
      <span v-if="cmdMessage" class="ml-auto text-ellipsis font12">
        {{ cmdMessage }}
      </span>
      <span v-else class="ml-auto color-warning font12">
        注：运行会以进程方式存在，记得点击停止运行。
      </span>
    </div>
    <el-scrollbar class="py5 pl5 flex-grow-1">
      <el-row :gutter="10" style="width: 100%">
        <el-col
          :xs="24"
          :sm="12"
          :md="8"
          :lg="6"
          v-for="item in loopItems"
          :key="item.id"
          class="mb10"
        >
          <el-card>
            <template #header>
              <div class="card-header flex">
                <span>{{ item.title }}</span>
                <el-tag v-if="item.tag" class="ml-auto">{{ item.tag }}</el-tag>
              </div>
            </template>
            <div class="card-content">
              <div class="flex align-center">
                <p>主程序包名：{{ item.filename }}</p>
                <el-tag
                  v-if="item.taskkill"
                  type="success"
                  class="font12 ml-auto"
                >
                  进程ID：{{ item.taskkill }}
                </el-tag>
              </div>
              <p>发布时间：{{ item.time_string || "暂无" }}</p>
              <div class="font12 flex align-center">
                <span class="flex-shrink">路径：</span>
                <el-link type="primary" class="font12" @click="openDir(item)">
                  {{ item.source }}
                </el-link>
              </div>
              <div class="font12 flex align-center">
                <span class="flex-shrink">API：</span>
                <el-link type="primary" class="font12" @click="openHost(item)">
                  {{ item.host || "未配置" }}
                </el-link>
              </div>
              <div class="font12 flex align-center mt5">
                <span class="flex-shrink">文件：</span>
                <span>{{ item.staticPath }}</span>
              </div>
            </div>
            <template #footer>
              <div
                style="gap: 2px 5px"
                class="button-margin-0 flex align-center flex-wrap"
              >
                <el-button
                  plain
                  :type="item.running ? 'danger' : 'success'"
                  :icon="SwitchButton"
                  :disabled="item.loading"
                  @click="handleRunRow(item)"
                >
                  {{ item.running ? "停止运行" : "开始运行" }}
                </el-button>

                <el-button
                  plain
                  type="warning"
                  :loading="item.loading"
                  :disabled="item.compression"
                  :icon="Position"
                  @click="handlePublishRow(item)"
                >
                  {{ item.loading ? "发布中" : "开始发布" }}
                </el-button>
                <el-button
                  plain
                  :icon="Files"
                  :type="item.fileRunning ? 'danger' : 'primary'"
                  :disabled="item.loading"
                  @click="handleExpress(item)"
                >
                  {{ item.fileRunning ? "停止静态文件" : "开启静态文件" }}
                </el-button>

                <!-- 强制换行 -->
                <div class="flex-basis" />

                <el-button
                  plain
                  :loading="item.compression"
                  :icon="Position"
                  @click="handleZip(item)"
                >
                  压缩全部
                </el-button>

                <el-button
                  plain
                  :loading="item.compression"
                  :icon="Position"
                  @click="handleZip(item, true)"
                >
                  压缩变更
                </el-button>

                <el-button
                  plain
                  type="primary"
                  :icon="Edit"
                  @click="handleEditRow(item)"
                >
                  编辑
                </el-button>
                <el-button
                  plain
                  type="danger"
                  :icon="Delete"
                  @click.prevent="handleRemoveRow(item)"
                >
                  删除
                </el-button>
              </div>
            </template>
          </el-card>
        </el-col>
      </el-row>
    </el-scrollbar>
    <RunningIndexDialog ref="dialogRef" />
  </div>
</template>
<style lang="scss">
.running-view {
  overflow: hidden;
  box-sizing: border-box;
  height: calc(100vh - 30px);
}
</style>
